package com.hmall.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.client.ItemFeignClient;
import com.hmall.common.dto.ItemDto;
import com.hmall.common.dto.PageResult;
import com.hmall.es.pojo.ItemDoc;
import com.hmall.es.pojo.RequestParams;
import com.hmall.es.service.ISearchService;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements ISearchService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ItemFeignClient itemFeignClient;

    /**
     * 自动补全功能
     *
     * @param key
     * @return
     */
    @Override
    public List<String> suggestionList(String key) throws IOException {
        //2.创建搜索请求对象，并指定索引库名称
        SearchRequest searchRequest=new SearchRequest("hmall");

        //3.设置参数
        searchRequest.source().suggest(new SuggestBuilder()
                .addSuggestion(
                        "mySuggestion",//自定义名称，方便后期取值使用
                        SuggestBuilders.completionSuggestion("suggestion")//表示自动补全哪个字段
                        .prefix(key)//表示搜索关键字
                        .skipDuplicates(true)//跳过重复的
                        .size(10)
                ));
        //1.执行es的搜索api
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


        //5.声明list对象
        List<String> suggestList=new ArrayList<>();


        //4.响应值
        Suggest suggest = searchResponse.getSuggest();
        CompletionSuggestion mySuggestion = suggest.getSuggestion("mySuggestion");
        for (CompletionSuggestion.Entry.Option option : mySuggestion.getOptions()) {
            suggestList.add(option.getText().toString());
        }

        //6.返回list集合
        return suggestList;
    }

    /**
     * 过滤筛选品牌和分类结果展示
     *
     * @param params
     * @return
     */
    @Override
    public Map<String, List<String>> filters(RequestParams params) throws IOException {

        //2.创建搜索请求对象，并指定索引库名称
        SearchRequest searchRequest=new SearchRequest("hmall");
        //设置只查询聚合，不查询商品信息
        searchRequest.source().size(0);
        //3.执行聚合操作
        aggBrandAndCategory(searchRequest);

        //1.查询es
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //4.执行结果筛选
        Aggregations aggregations = searchResponse.getAggregations();

        Map<String, List<String>> resultMap=new HashMap<>();
        //5.聚合结果封装
        chouquAgg(aggregations, resultMap);

        return resultMap;
    }



    /**
     * 聚合结果封装抽取的方法
     * @param aggregations
     * @param resultMap
     */
    private void chouquAgg(Aggregations aggregations, Map<String, List<String>> resultMap) {
        //封装品牌操作
        List<String> brandList=new ArrayList<>();

        Terms brandTermsAgg = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> brandBuckets = brandTermsAgg.getBuckets();
        for (Terms.Bucket bucket : brandBuckets) {
            String brandKey = (String) bucket.getKey();
            brandList.add(brandKey);
        }

        //封装分类操作
        List<String> categoryList=new ArrayList<>();

        Terms categoryTermsAgg = aggregations.get("categoryAgg");
        List<? extends Terms.Bucket> categoryBuckets = categoryTermsAgg.getBuckets();
        for (Terms.Bucket bucket : categoryBuckets) {
            String brandKey = (String) bucket.getKey();
            categoryList.add(brandKey);
        }

        resultMap.put("brand",brandList);
        resultMap.put("category",categoryList);
    }

    /**
     * 抽取的聚合方法
     */
    private void aggBrandAndCategory(SearchRequest searchRequest) {
        //给品牌聚合
        searchRequest.source().aggregation(
                AggregationBuilders.terms("brandAgg")//去名称，名称可以随意起
                .field("brand")
                .size(20)
        );
        //给分类聚合
        searchRequest.source().aggregation(
                AggregationBuilders.terms("categoryAgg")//去名称，名称可以随意起
                        .field("category")
                        .size(20)
        );
    }





    /**
     * 基本的搜索查询
     *
     * @param params
     * @return
     */
    @Override
    public PageResult list(RequestParams params) throws IOException {
        //2.创建搜索请求对象，并指定索引库名称
        SearchRequest searchRequest=new SearchRequest("hmall");

        //4.创建布尔查询
        BoolQueryBuilder boolQuery= QueryBuilders.boolQuery();
        //执行DSL语句
            //4.0 关键字过滤,模糊查询
        if(StringUtils.isBlank(params.getKey())){//如果为空,则查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else {//如果不为空,则精确查询
            boolQuery.must(QueryBuilders.matchQuery("all",params.getKey()));
        }

            //4.1 执行品牌过滤，精确查询
        if(StringUtils.isNotBlank(params.getBrand())){
            boolQuery.filter(QueryBuilders.termQuery("brand",params.getBrand()));
        }

            //4.2 分类过滤
        if(StringUtils.isNotBlank(params.getCategory())){
            boolQuery.filter(QueryBuilders.termQuery("category",params.getCategory()));
        }

            //4.3 价格过滤
        if(params.getMinPrice()!=null && params.getMaxPrice()!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
        }
        //3.创建查询
        searchRequest.source().query(boolQuery);

            //5 分页设置
        Integer page=(params.getPage()-1)*params.getSize();
        searchRequest.source().from(page).size(params.getSize());

            //6 排序
        if(!params.getSortBy().equals("default")){
            if(params.getSortBy().equals("price")){
                searchRequest.source().sort("price", SortOrder.DESC);
            }else{
                searchRequest.source().sort("sold", SortOrder.DESC);
            }
        }
            //7 高亮设置
        searchRequest.source().highlighter(
                new HighlightBuilder()
                        .field("name")
                .preTags("<font style='color:red'>")
                .postTags("</font>")
                .requireFieldMatch(false)
        );

        //1.调用es的search方法
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //10.创建一个空的list集合，用来封装数据
        List itemDocList=new ArrayList();

        //8.结果数据分析
            //8.1 获取所有的总条数
        long total = searchResponse.getHits().getTotalHits().value;
            //8.2 获取所有命中的商品信息
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            //获取source
            String itemDocStr = hit.getSourceAsString();

            //类型转化成对象
            ItemDoc itemDoc = JSON.parseObject(itemDocStr, ItemDoc.class);

            //进行高亮展示
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if(highlightFields!=null && highlightFields.size()>0){
                HighlightField highlightField = highlightFields.get("name");
                String nameHight = highlightField.getFragments()[0].toString();
                itemDoc.setName(nameHight);
            }

            itemDocList.add(itemDoc);
        }

        //9.封装数据
        PageResult pageResult=new PageResult();
        pageResult.setTotal(total);
        pageResult.setList(itemDocList);

        return pageResult;
    }


    /**
     * 新增ES索引库的文档操作
     *
     * @param id
     */
    @Override
    public void saveItemToES(Long id) {
        //2.创建索引请求对象
        IndexRequest indexRequest=new IndexRequest("hmall").id(id.toString());

        //3.添加数据

        //我们要根据id去查询到ItemDto对象值，利用Feign远程调用
        ItemDto itemDto=itemFeignClient.findById(id);
        //对象属性拷贝
        ItemDoc itemDoc=new ItemDoc(itemDto);

        String source=JSON.toJSONString(itemDoc);
        indexRequest.source(source, XContentType.JSON);

        //1.调用新增ES的api
        try {
            restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除ES索引库中的文档
     *
     * @param id
     */
    @Override
    public void delItemFromES(Long id) {
        DeleteRequest deleteRequest=new DeleteRequest("hmall").id(id.toString());
        try {
            restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
